<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- base href="http://www.codeslinger.co.uk/pages/projects/gameboy/banking.html" --></head><body><div style="background:#fff;border:1px solid #999;margin:-1px -1px 0;padding:0;"><div style="background:#ddd;border:1px solid #999;color:#000;font:13px arial,sans-serif;font-weight:normal;margin:12px;padding:8px;text-align:left">Esta es la versión en caché de <a href="http://www.codeslinger.co.uk/pages/projects/gameboy/banking.html" style="text-decoration: underline; color: rgb(0, 0, 204);">http://www.codeslinger.co.uk/pages/projects/gameboy/banking.html</a>
 de Google. Se trata de una captura de pantalla de la página tal como 
esta se mostraba el 19 Jun 2011 10:12:40 GMT. Es posible que la <a href="http://www.codeslinger.co.uk/pages/projects/gameboy/banking.html" style="text-decoration: underline; color: rgb(0, 0, 204);">página</a> haya sufrido modificaciones durante este tiempo. <a href="http://www.google.com/intl/es/help/features_list.html#cached" style="text-decoration: underline; color: rgb(0, 0, 204);">Más información</a><br><br><div style="float:right"><a href="http://webcache.googleusercontent.com/search?q=cache:dLc5pCHMGrMJ:www.codeslinger.co.uk/pages/projects/gameboy/banking.html+codeslinger+gameboy+memory+control&amp;hl=es&amp;gl=ar&amp;strip=1" style="text-decoration: underline; color: rgb(0, 0, 204);">Versión de solo texto</a></div>
<div>Se han resaltado estos términos de búsqueda: <span style="background:#ffff66;color:black;font-weight:bold">codeslinger</span>&nbsp;<span style="background:#a0ffff;color:black;font-weight:bold">gameboy</span>&nbsp;<span style="background:#99ff99;color:black;font-weight:bold">memory</span>&nbsp;<span style="background:#ff9999;color:black;font-weight:bold">control</span>&nbsp;&nbsp;</div></div></div><div style="position:relative">



<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="description" content="_Emulation Programming_">
<meta name="keywords" content="_Emulation,Chip8,Gameboy,NES,SNES,Genesis,Master,System,Mega,Drive_">
<meta name="author" content="CodeSlinger /">
<link rel="stylesheet" type="text/css" href="4%20ROM%20and%20RAM%20banking_files/css.css" media="screen,projection" title="CodeSlinger (screen)">
<link rel="stylesheet" type="text/css" href="4%20ROM%20and%20RAM%20banking_files/print.css" media="print">
<title>codeslinger.co.uk</title>



<div>
<br><br>
</div>

<div id="container">
<div id="logo">
<h1><a href="http://www.codeslinger.co.uk/index.html"><b style="color:black;background-color:#ffff66">codeslinger</b>.co.uk</a></h1>
</div>

<div id="navitabs">
<h2 class="hide">Site menu:</h2>
<a class="navitab" href="http://www.codeslinger.co.uk/index.php">Home</a><span class="hide"> | </span>
<a class="navitab" href="http://www.codeslinger.co.uk/pages/basics.html">Basics</a><span class="hide"> | </span>
<a class="navitab" href="http://www.codeslinger.co.uk/pages/projects/zuko.html">Zuko</a><span class="hide"> | </span>
<a class="navitab" href="http://www.codeslinger.co.uk/pages/projects/megadrive.html">Mega Drive/Genesis</a><span class="hide"> | </span>
<a class="navitab" href="http://www.codeslinger.co.uk/pages/projects/mastersystem.html">Master System</a><span class="hide"> | </span>
<a class="activenavitab" href="http://www.codeslinger.co.uk/pages/projects/gameboy.html"><b style="color:black;background-color:#a0ffff">Gameboy</b></a><span class="hide"> | </span>
<a class="navitab" href="http://www.codeslinger.co.uk/pages/projects/chip8.html">Chip8</a><span class="hide"> | </span>
<a class="navitab" href="http://www.codeslinger.co.uk/pages/blog/index.html">Blog</a><span class="hide"> | </span>
</div>
	
<div id="desc">
<h2><b style="color:black;background-color:#ffff66">codeslinger</b>.co.uk</h2>
<p> <b style="color:black;background-color:#a0ffff">Gameboy</b> - Rom and Ram Banking.</p>

</div>

<div id="main">

<h3><b>Detecting Rom Bank Mode:</b></h3>
<p>

There are two types of ROM banking that I have emulated, MBC1 and MBC2. 
The majority of the games (80%) are MBC1 so to have a decent emulator 
this is a must to emulate. Some games like Tetris and Bubble Ghost
dont use ROM banking at all. They just load the entire game into <b style="color:black;background-color:#99ff99">memory</b> region 0x0-0x8000 and never need to swap <b style="color:black;background-color:#99ff99">memory</b> in and out. To detect what ROM mode the game is you have to read <b style="color:black;background-color:#99ff99">memory</b> 0x147 after the
game has been loaded into <b style="color:black;background-color:#99ff99">memory</b>. If 0x147 is 0 then the game has no <b style="color:black;background-color:#99ff99">memory</b>
 banking (like tetris), however if it is 1,2 or 3 then it is MBC1 and if
 it is 5 or 6 then it is MBC2. This gives the following code:

</p><p class="code">

m_MBC1 = false ; <br>
m_MBC2 = false ; <br><br>
switch (m_CartridgeMemory[0x147])<br>
{<br>
&nbsp;&nbsp; case 1 : m_MBC1 = true ; break <br>
&nbsp;&nbsp; case 2 : m_MBC1 = true ; break <br>
&nbsp;&nbsp; case 3 : m_MBC1 = true ; break <br>
&nbsp;&nbsp; case 5 : m_MBC2 = true ; break <br>
&nbsp;&nbsp; case 6 : m_MBC2 = true ; break <br>
&nbsp;&nbsp; default : break ; <br>
}
</p>

We also need a variable declaration to specify which ROM bank is currently loaded into internal <b style="color:black;background-color:#99ff99">memory</b> address 0x4000-0x7FFF. As ROM Bank 0
is fixed into <b style="color:black;background-color:#99ff99">memory</b>
 address 0x0-0x3FFF this variable should never be 0, it should be at 
least 1. We need to initialize this variable on emulator load to 1.

<p class="code">
 m_CurrentROMBank = 1 ; // this is type BYTE
</p>

<p></p>

<h3><b>Detecting RAM Banking:</b> </h3>
<p>
 Cartridge <b style="color:black;background-color:#99ff99">memory</b> 
address 0x148 tells how much RAM banks the game has. The maximum is 4. 
The size of 1 RAM bank is 0x2000 bytes so if we have an array of size
 0x8000 this is enough space for all the RAM banks. Like ROM banking we 
also need a variable to point at which RAM bank the game is using 
between values of 0-3. This
 gives us the following declarations.
 
 </p><p class="code">
 
 BYTE m_RAMBanks[0x8000] ;<br>
 BYTE m_CurrentRAMBank ;<br>
 
 </p>
 
 We then need to initialize these like so:
 
  <p class="code">
 memset(&amp;m_RAMBanks,0,sizeof(m_RAMBanks) ;<br>
 m_CurrentRAMBank=0;
  
 </p>
 <strong>RAM Banking is not used in MBC2! Therefore m_CurrentRAMBank will always be 0!</strong>
<p></p>

<h3><b>Controlling reading from ROM and RAM:</b> </h3>
<p>
As stated in the section called <a href="http://www.codeslinger.co.uk/pages/projects/gameboy/memory.html"><b style="color:black;background-color:#99ff99">Memory</b> <b style="color:black;background-color:#ff9999">Control</b> and Map</a> we need to <b style="color:black;background-color:#ff9999">control</b> Reading and Writing to the internal <b style="color:black;background-color:#99ff99">memory</b>. The main reason
to <b style="color:black;background-color:#ff9999">control</b> the reading is to make sure everything reads from the correct ROM and RAM banks. This will give us the following function:

</p><p class="code">
// read <b style="color:black;background-color:#99ff99">memory</b> should never modify member variables hence const<br>
BYTE Emulator::ReadMemory(WORD address) const <br>
{<br>
&nbsp;&nbsp; // are we reading from the rom <b style="color:black;background-color:#99ff99">memory</b> bank?<br>
&nbsp;&nbsp; if ((address&gt;=0x4000) &amp;&amp; (address &lt;= 0x7FFF))<br>
&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp; WORD newAddress = address - 0x4000 ;<br>
&nbsp;&nbsp;&nbsp;&nbsp; return m_CartridgeMemory[newAddress + (m_CurrentROMBank*0x4000)] ;<br>
&nbsp;&nbsp; }<br><br>

&nbsp;&nbsp; // are we reading from ram <b style="color:black;background-color:#99ff99">memory</b> bank? <br>
&nbsp;&nbsp; else if ((address &gt;= 0xA000) &amp;&amp; (address &lt;= 0xBFFF))<br>
&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp; WORD newAddress = address - 0xA000 ;<br>
&nbsp;&nbsp;&nbsp;&nbsp; return m_RAMBanks[newAddress + (m_CurrentRAMBank*0x2000)] ;<br>

&nbsp;&nbsp; }<br><br>

&nbsp;&nbsp; // else return <b style="color:black;background-color:#99ff99">memory</b><br>
&nbsp;&nbsp; return m_Rom[address] ;<br>

}
</p>

That was actually pretty easy wasn't it? Hopefully now you can see why 
you must ALWAYS you ReadMemory and WriteMemory when accessing internal <b style="color:black;background-color:#a0ffff">gameboy</b> <b style="color:black;background-color:#99ff99">memory</b>

<p></p>

<h3><b>Changing the current ROM and RAM Banks:</b> </h3>
<p>

Now we know how to read from the correct <b style="color:black;background-color:#99ff99">memory</b> banks but how does the game request the banks to be changed? In my opinion this is one of the most difficult
parts of the <b style="color:black;background-color:#a0ffff">gameboy</b> emulation. It isnt difficult to code but it is difficult to make sense of what you have to do. The way it works is the <b style="color:black;background-color:#a0ffff">gameboy</b> attempts to
write directy to ROM but our WriteMemory function will trap it and decypher why it is trying to write to ROM. Depending on the <b style="color:black;background-color:#99ff99">memory</b>
 address of where it is trying
to write to ROM we need to take different action. If the address is 
between 0x2000-0x4000 then it is a ROM bank change. If the address is 
0x4000-0x6000 then it is a RAM bank change or
a ROM bank change depending on what current ROM/RAM mode is selected 
(explained in a minute). If the value is between 0x0-0x2000 then it 
enables RAM bank writing (also explained in a minute).
We can now change the ROM part of our WriteMemory function to this:

</p><p class="code">
void Emulator::WriteMemory(WORD address, BYTE data) <br>
{<br>
&nbsp;&nbsp; if (address &lt; 0x8000)<br>
&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp; HandleBanking(address,data) ; <br>
&nbsp;&nbsp; }<br><br>

&nbsp;&nbsp; else if ((address &gt;= 0xA000) &amp;&amp; (address &lt; 0xC000))<br>
&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp; if (m_EnableRAM)<br>
&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WORD newAddress = address - 0xA000 ;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_RAMBanks[newAddress + (m_CurrentRAMBank*0x2000)] = data ;<br>
&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp; }<br><br>
&nbsp;&nbsp; // the rest of this function carries on as before<br>
}<br><br>

///////////////////////////////////////////////////////////////// <br><br>

void Emulator::HandleBanking(WORD address, BYTE data) <br>
{<br>
&nbsp;&nbsp; // do RAM enabling<br>
&nbsp;&nbsp; if (address &lt; 0x2000)<br>
&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp; if (m_MBC1 || m_MBC2)<br>
&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DoRamBankEnable(address,data) ;<br>
&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp; }<br><br>

&nbsp;&nbsp; // do ROM bank change <br>
&nbsp;&nbsp; else if ((address &gt;= 0x200) &amp;&amp; (address &lt; 0x4000))<br>
&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp; if (m_MBC1 || m_MBC2)<br>
&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DoChangeLoROMBank(data) ; <br>
&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp; }<br><br>

&nbsp;&nbsp; // do ROM or RAM bank change<br>
&nbsp;&nbsp; else if ((address &gt;= 0x4000) &amp;&amp; (address &lt; 0x6000))<br>
&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp; // there is no rambank in mbc2 so always use rambank 0<br>
&nbsp;&nbsp;&nbsp;&nbsp; if (m_MBC1)<br>
&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(m_ROMBanking)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DoChangeHiRomBank(data) ; <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DoRAMBankChange(data) ; <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br>
&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp; } <br><br>

&nbsp;&nbsp; // this will change whether we are doing ROM banking<br>
&nbsp;&nbsp; // or RAM banking with the above if statement<br>
&nbsp;&nbsp; else if ((address &gt;= 0x6000) &amp;&amp; (address &lt; 0x8000))<br>
&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp; if (m_MBC1)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DoChangeROMRAMMode(data) ;<br>
&nbsp;&nbsp; }<br><br>
}<br>
</p>

Read on for a full explanation of what the above code means


<p></p>

<h3><b>Enabling RAM:</b> </h3>
<p>
In order to write to RAM banks the game must specifically request that ram bank writing is enabled. It does this
by attempting to write to internal ROM address between 0 and 0x2000. For MBC1 if the lower nibble of the data the game
is writing to <b style="color:black;background-color:#99ff99">memory</b> is 0xA then ram bank writing is enabled else if the lower nibble is 0 then ram bank writing is disabled.
MBC2 is exactly the same except there is an additional clause that bit 4 of the address byte must be 0. This gives the following
function:

</p><p class="code">
void Emulator::DoRAMBankEnable(WORD address, BYTE data)<br>
{<br>
&nbsp;&nbsp; if (m_MBC2)<br>
&nbsp;&nbsp; {<br> 
&nbsp;&nbsp;&nbsp;&nbsp; if (TestBit(address,4) == 1) return ;<br>
&nbsp;&nbsp; }<br><br>

&nbsp;&nbsp; BYTE testData = data &amp; 0xF ;<br>
&nbsp;&nbsp; if (testData == 0xA)<br>
&nbsp;&nbsp;&nbsp;&nbsp; m_EnableRAM = true ;<br>
&nbsp;&nbsp; else if (testData == 0x0)<br>
&nbsp;&nbsp;&nbsp;&nbsp; m_EnableRAM = false ;<br>
}
</p>

<p></p>

<h3><b>Changing ROM Banks Part 1:</b> </h3>
<p>
If the <b style="color:black;background-color:#99ff99">memory</b> bank is MBC1 then there is two parts to changing the current rom bank. The first way is if the game
writes to <b style="color:black;background-color:#99ff99">memory</b> address 0x2000-0x3FFF then it changes the lower 5 bits of the current rom bank but not bits 5 and 6.
The second way is writing to <b style="color:black;background-color:#99ff99">memory</b>
 address 0x4000-0x5FFF during rombanking mode (explained later) which 
only changes bits 5 and 6
not bits 0-4. So combining these two methods you can change bits 0-6 of 
which rom bank is currently in use. However if the game
is using MBC2 then this is much easier. If the game writes to address 
0x2000-0x3FFF then the current ram bank changes bits 0-3 and
bits 5-6 are never set. This means writing to address 0x4000-0x5FFF in 
MBC2 mode does nothing. This section explains what happens
when the game writes to <b style="color:black;background-color:#99ff99">memory</b> address 0x2000-0x3FFF.

</p><p class="code">

void Emulator::DoChangeLoROMBank(BYTE data)<br>
{<br>
&nbsp;&nbsp; if (m_MBC2)<br>
&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp; m_CurrentROMBank = data &amp; 0xF ;<br>
&nbsp;&nbsp;&nbsp;&nbsp; if (m_CurrentROMBank == 0) m_CurrentROMBank++ ;<br>
&nbsp;&nbsp;&nbsp;&nbsp; return ;<br>
&nbsp;&nbsp; }<br><br>

&nbsp;&nbsp; BYTE lower5 = data &amp; 31 ;<br>
&nbsp;&nbsp; m_CurrentROMBank &amp;= 224; // turn off the lower 5<br>
&nbsp;&nbsp; m_CurrentROMBank |= lower5 ;<br>
&nbsp;&nbsp; if (m_CurrentROMBank == 0) m_CurrentROMBank++ ;<br>
} 

</p>

So if we're using MBC2 then the current rom bank becomes the lower nibble of data. However if we are using MBC1
then we must set the lower 5 bits of current rom bank to the lower 5 bits of data whilst keeping the upper 3 bits the same.
You may be wondering why I increment m_CurrentROMBank if it is set to 0. The reason is that rom bank 0 is static and can always
be found in <b style="color:black;background-color:#99ff99">memory</b> address 0x000-0x4000 so rom bank 0 should never be loaded into <b style="color:black;background-color:#99ff99">memory</b> 0x4000-0x8000. If m_CurrentROMBank is ever
set to 0 then it is treated as rombank 1 so rombank 1 or greater will reside in the bank region 0x4000-0x8000.

<p></p>

<h3><b>Changing ROM Banks Part 2:</b> </h3>
<p>
As just stated there are two ways to change the current rom bank in MBC1 mode. This shows how to change the bits 5 and 6
when writing to <b style="color:black;background-color:#99ff99">memory</b> address 0x4000-0x6000 and m_RomBanking is true (explained later)

</p><p class="code">
void Emulator::DoChangeHiRomBank(BYTE data)<br>
{<br>
&nbsp;&nbsp; // turn off the upper 3 bits of the current rom<br>
&nbsp;&nbsp; m_CurrentROMBank &amp;= 31 ; <br><br>

&nbsp;&nbsp; // turn off the lower 5 bits of the data<br>
&nbsp;&nbsp; data &amp;= 224 ; <br>
&nbsp;&nbsp; m_CurrentROMBank |= data ;<br>
&nbsp;&nbsp; if (m_CurrentROMBank == 0) m_CurrentROMBank++ ;<br>
}
</p>

<p></p>

<h3><b>Changing RAM Banks:</b> </h3>
<p>
You cannot change RAM Banks in MBC2 as that has external ram on the cartridge. To change RAM Banks in MBC1 the game
must again write to <b style="color:black;background-color:#99ff99">memory</b> address 0x4000-0x6000 but this time m_RomBanking must be false (explained later). The current
ram bank gets set to the lower 2 bits of the data like so:


</p><p class="code">
void Emulator::DoRAMBankChange(BYTE data)<br>
{<br>
&nbsp;&nbsp; m_CurrentRAMBank = data &amp; 0x3 ;<br>
}
</p>

<h3><b>Selecting either ROM or RAM banking mode:</b> </h3>
<p>
Finally the last part of this banking marathon is this m_RomBanking I keep going on about. This variable is responsible
for how to act when the game writes to <b style="color:black;background-color:#99ff99">memory</b> address 0x4000-0x6000. This variable defaults to true but is changes during
MBC1 mode when the game writes to <b style="color:black;background-color:#99ff99">memory</b>
 address 0x6000-0x8000. If the least significant bit of the data being 
written to this
address range is 0 then m_RomBanking is set to true, otherwise it is set
 to false meaning there is about to be a ram bank change.
It is important to set m_CurrentRAMBank to 0 whenever you set 
m_RomBanking to true because the <b style="color:black;background-color:#a0ffff">gameboy</b> can only use rambank 0 in this
mode.

</p><p class="code">
void Emulator::DoChangeROMRAMMode(BYTE data)<br>
{<br>
&nbsp;&nbsp; BYTE newData = data &amp; 0x1 ; <br>
&nbsp;&nbsp; m_RomBanking = (newData == 0)?true:false ;<br>
&nbsp;&nbsp; if (m_RomBanking)<br>
&nbsp;&nbsp;&nbsp;&nbsp; m_CurrentRAMBank = 0 ; <br>
}
</p>

That is the end of the banking section. Next is <a href="http://www.codeslinger.co.uk/pages/projects/gameboy/timers.html">The Timers</a> section which is easier than this!

<br><br>
<p></p>

</div>

<div id="sidebar">
<h3><b style="color:black;background-color:#a0ffff">Gameboy</b> Emulation:</h3>

<p>
<a class="sidelink" href="http://www.codeslinger.co.uk/pages/projects/gameboy/beginning.html">Getting Started</a><span class="hide"> | </span>
<a class="sidelink" href="http://www.codeslinger.co.uk/pages/projects/gameboy/hardware.html">The Hardware</a><span class="hide"> | </span>
<a class="sidelink" href="http://www.codeslinger.co.uk/pages/projects/gameboy/memory.html"><b style="color:black;background-color:#99ff99">Memory</b> <b style="color:black;background-color:#ff9999">Control</b> and Map</a><span class="hide"> | </span>
<a class="sidelink" href="http://www.codeslinger.co.uk/pages/projects/gameboy/banking.html">ROM and RAM Banking</a><span class="hide"> | </span>
<a class="sidelink" href="http://www.codeslinger.co.uk/pages/projects/gameboy/timers.html">The Timers</a><span class="hide"> | </span>
<a class="sidelink" href="http://www.codeslinger.co.uk/pages/projects/gameboy/interupts.html">Interupts</a><span class="hide"> | </span>
<a class="sidelink" href="http://www.codeslinger.co.uk/pages/projects/gameboy/lcd.html">LCD</a><span class="hide"> | </span>
<a class="sidelink" href="http://www.codeslinger.co.uk/pages/projects/gameboy/dma.html">DMA Transfer</a><span class="hide"> | </span>
<a class="sidelink" href="http://www.codeslinger.co.uk/pages/projects/gameboy/graphics.html">Graphic Emulation</a><span class="hide"> | </span>
<a class="sidelink" href="http://www.codeslinger.co.uk/pages/projects/gameboy/joypad.html">Joypad Emulation</a><span class="hide"> | </span>
<a class="sidelink" href="http://www.codeslinger.co.uk/pages/projects/gameboy/opcodes.html">Opcode Examples</a><span class="hide"> | </span>
<a class="sidelink" href="http://www.codeslinger.co.uk/pages/projects/gameboy/finished.html">Finished Product</a><span class="hide"> | </span>

</p>

</div>
    
<div id="footer">
Copyright © 2008 <b style="color:black;background-color:#ffff66">codeslinger</b>.co.uk
</div>

</div>




</div></body></html>